package logic.ships.hunter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import main.listeners.ListenerContainer;
import map.spawnStation.SpawnPosition;
import networking.messages.createMessages.HunterCreateMessage;
import networking.messages.createMessages.ShipType;
import networking.messages.syncMessages.ValueType;

import input.pilot.MoveToMissionController;
import input.pilot.ResetRotationController;
import interfaces.hud.targetInfos.AbsTargetArrow;
import interfaces.spawnMenu.spawnMap.SpawnButtonContent.SpawnType;

import logic.SpawnLocation;
import logic.common.player.Player;
import logic.common.team.Team;
import logic.items.EnergyCell;
import logic.items.EnergyCellProperties;
import logic.items.Item;
import logic.items.ItemProperties;
import logic.items.ItemType;
import logic.items.RepairItem;
import logic.items.RepairItemProperties;
import logic.items.ShieldItem;
import logic.items.ShieldItemProperties;
import logic.nodes.collision.DummyType;
import logic.ships.Ship;
import logic.ships.equipmentListener.EquipmentListener;
import logic.ships.equipmentListener.ItemChangedEvent;
import logic.ships.moveableShip.MovableShip;
import logic.weapons.Weapon;
import logic.weapons.WeaponFire;
import logic.weapons.WeaponType;
import logic.weapons.projectileWeapons.ProjectileWeapon;
import logic.weapons.projectileWeapons.heatSeeker.HeatSeeker;

import com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage;
import com.jme.input.controls.controller.RotationController;
import com.jme.scene.Controller;
import com.jme.scene.Node;

public class Hunter extends MovableShip implements SpawnPosition {
	
	private static final long serialVersionUID = 1L;
	
	private ResetRotationController resetRotController;
	private HashMap<ItemType, List<Item>> items;
	private CounterMeasure counterMeasure;
	private Player pilot;
	private AbsTargetArrow arrow;
	private ListenerContainer heatSeekerListeners;
	private Collection<Controller> hunterFlightControllers;
	private MoveToMissionController moveToMissionController;
	
	public Hunter(String modelPath, String title, Node model, HunterProperties properties, Team team) {
		this(modelPath, title, model, properties, team, DummyType.None);
	}
	
	public Hunter(String modelPath, String title, Node model, HunterProperties properties, Team team, 
			DummyType dummyType) {
		super(modelPath, title, model, properties, team, dummyType);
		
		hunterFlightControllers = new ArrayList<Controller>();
		
		heatSeekerListeners = new ListenerContainer(HeatSeekerListener.class);
		
		counterMeasure = new CounterMeasure(this);
		attachChild(counterMeasure);
		
		items = new HashMap<ItemType, List<Item>>();
	}
	
	public void setMoveToMissionController(MoveToMissionController c) {
		moveToMissionController = c;
		addController(moveToMissionController);
	}
	
	public MoveToMissionController getMoveToMissionController() { return moveToMissionController; }
	
	public void removeMoveToMissionController() {
		removeController(moveToMissionController);
		moveToMissionController = null;
	}
	
	@Override
	protected void updateMoveControllers() {
		super.updateMoveControllers();
		
		for(Controller c : hunterFlightControllers) {
			if(c instanceof RotationController) {
				RotationController r = (RotationController)c;
				r.setMultiplier(getTurnSpeed());
			}
		}
	}
	
	public void addHunterFlightController(Controller c) {
		hunterFlightControllers.add(c);
		addController(c);
	}
	
	public void removeHunterFlightControllers() {
		for(Controller c : hunterFlightControllers) {
			removeController(c);
		}
		
		hunterFlightControllers.clear();
	}
	
	public void removeHunterFlightController(Controller c) {
		removeController(c);
		hunterFlightControllers.remove(c);
	}
	
	public void addHeatSeekerListener(HeatSeekerListener l) { heatSeekerListeners.addListener(l); }
	
	public void removeHeatSeekerListener(HeatSeekerListener l) { heatSeekerListeners.removeListener(l); }
	
	public void setArrow(AbsTargetArrow arrow) { this.arrow = arrow; }
	
	public AbsTargetArrow getArrow() { return arrow; }
	
	@Override
	public void setMarked(boolean mark) {
		super.setMarked(mark);
		arrow.setMarked(mark);
	}
	
	public void restockProjectiles(WeaponType type, int amount) {
		assert(amount > 0);
		
		List<Weapon> weaponList = weapons.get(type);
		int weaponIndex = 0;
		
		for(int i = 0; i < amount; i++) {
			Weapon w = weaponList.get(weaponIndex);
			assert(w instanceof ProjectileWeapon);
			
			ProjectileWeapon weapon = (ProjectileWeapon)w;
			if(weapon.isFullyLoaded()) i--;
			else weapon.addProjectile();
			
			weaponIndex++;
			if(weaponIndex >= weaponList.size()) weaponIndex = 0;
		}
	}
	
	@Override
	public void receiveDamage(int damage) {
		super.receiveDamage(damage);
		if(getCurrentHP() == 0) killPlayer();
	}
	
	private void killPlayer() {
		pilot.increaseDeaths();
	}
	
	@Override
	public void receiveDamage(WeaponFire weaponFire) {
		super.receiveDamage(weaponFire);
		
		Weapon w = weaponFire.getWeapon();
		if(getCurrentHP() == 0) {
			
			Ship ship = weaponFire.getShip();
			if(ship instanceof Hunter) {
				Hunter hunter = (Hunter)ship;
				Player initiator = hunter.getPilot();
				if(!pilot.isTeamMate(initiator)) initiator.addKill();
				
				w.addKill();
				initiator.killed(pilot, w);
			}
		}
		w.addHit();
	}
	
	public void setHunterProperties(HunterProperties props) { settings = props; }
		
	public ResetRotationController getResetRotController() { return resetRotController; }
	
	public void setResetRotContorller(ResetRotationController c) {
		removeResetRotController();
		resetRotController = c;
		addController(resetRotController);
	}
	
	public void removeResetRotController() {
		if(resetRotController != null) {
			removeController(resetRotController);
			resetRotController = null;
		}
	}
	
	public void setPilot(Player pilot) { this.pilot = pilot; }
	
	public Player getPilot() { return pilot; }
	
	public void addItem(ItemProperties itemProps) {
		ItemType type = itemProps.getType();
		List<Item> list = items.get(type);
		if(list == null) {
			list = new LinkedList<Item>();
			items.put(type, list);
		}
		
		Item item = createItem(itemProps);
		list.add(item);
		
		equipmentListeners.callMethod(EquipmentListener.ITEMADDED, new ItemChangedEvent(item));
	}
	
	private Item createItem(ItemProperties props) {
		ItemType type = props.getType();
		switch(type) {
			case Repair : return new RepairItem(this, (RepairItemProperties)props);
			case Shields : return new ShieldItem(this, (ShieldItemProperties)props);
			case Energy : return new EnergyCell(this, (EnergyCellProperties)props);
		}
		return null;
	}
	
	public void removeItem(ItemType type) {
		List<Item> list = items.get(type);
		Item item = list.remove(0);
		
		equipmentListeners.callMethod(EquipmentListener.ITEMREMOVED, new ItemChangedEvent(item));
	}
	
	public List<Item> getItems(ItemType type) { return items.get(type); }
	
	public int getItemsAmount(ItemType type) {
		List<Item> items = getItems(type);
		if(items == null) return 0;
		return items.size();
	}
	
	public void useItem(ItemType type) {
		List<Item> list = items.get(type);
		if(list != null && !list.isEmpty()) {
			Item item = list.remove(0);
			if(item != null) {
				item.use();
				
				equipmentListeners.callMethod(EquipmentListener.ITEMUSED, new ItemChangedEvent(item));
			}
		}
	}
	
	@Override
	public int getDestroyMoney() { return 150; }
	
	public HunterProperties getHunterProperties() { return (HunterProperties)settings; }
	
	public void addAttackingHeatSeeker(HeatSeeker heatSeeker) {
		counterMeasure.addHeatSeeker(heatSeeker);
		
		heatSeekerListeners.callMethod(HeatSeekerListener.HEATSEEKERFIRED, new HeatseekerFiredEvent(true));
	}
	
	public void useDummyCounterMeasures() { counterMeasure.use(true); }
	
	public void useCounterMeasures() { counterMeasure.use(); }
	
	public void removeHeatSeeker(HeatSeeker heatSeeker) {
		counterMeasure.removeHeatSeeker(heatSeeker);
		
		heatSeekerListeners.callMethod(HeatSeekerListener.HEATSEEKERFIRED, new HeatseekerFiredEvent(false));
	}
	
	public boolean hasAttackingHeatSeeker() { return !counterMeasure.getHeatSeekers().isEmpty(); }
	
	public float getCounterMeasureLoad() { return counterMeasure.getReloadTime(); }
	
	public void updateCMLoad(float newValue) {
		valueManager.changeValue(ValueType.CounterMeasures, newValue);
	}
	
	@Override
	public void restore() {
		super.restore();
		counterMeasure.restore();
		restockWeaponsFull(WeaponType.HeatSeeker);
		restockWeaponsFull(WeaponType.Missile);
	}
	
	private void restockWeaponsFull(WeaponType type) {
		List<Weapon> weapons = getWeapons(type);
		for(Weapon w : weapons) {
			ProjectileWeapon pWeapon = (ProjectileWeapon)w;
			if(!pWeapon.isFullyLoaded()) pWeapon.reloadFully();
		}
	}
	
	@Override
	protected float getDistanceFactor() { return 0.5f; }
	
	public String getHunterName() { return getHunterProperties().getHunterName(); }
	
	@Override
	public SynchronizeCreateMessage getCreateMessage() {
		return new HunterCreateMessage(getName(), team.getFraction(), objectID, getPilot().getID());
	}
	
	@Override
	protected ShipType getShipType() { return ShipType.Hunter; }
	
	@Override
	public float getActionRadius() { return 40f; }
	
	@Override
	public SpawnLocation getSpawnLocation() { return pilot.getPlayerSpawnLocation(); }
	
	@Override
	public SpawnType getSpawnType() { return SpawnType.Squad; }
	
	@Override
	public ListenerContainer getListenerContainer(Class<?> listenerClass) {
		if(listenerClass == HeatSeekerListener.class) return heatSeekerListeners;
		return super.getListenerContainer(listenerClass);
	}
}